package netty.lora;

import com.google.common.collect.Sets;
import com.google.gson.Gson;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoop;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import netty.DataRequest;
import netty.DataResponse;
import netty.util.ByteUtil;
import netty.util.ModBusUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

@Sharable
class LoraDataHandler extends ChannelInboundHandlerAdapter {

    private static final Logger LOG = LoggerFactory.getLogger(LoraDataHandler.class);
    private LoraMonitor client;
    ThreadLocalRandom random = ThreadLocalRandom.current();
    ScheduledExecutorService schedule = Executors.newScheduledThreadPool(4);
    private int iceMachineState;
    private int cycles;

    private Set<String> fermentCabinetStatus;
    private Set<String> fermentCabinetAlarms;

    private static final String CHAIN_OVEN_PARTS = "0006BDA7";
    private static final String ICE_MACHINE_MD = "0006BD9A";
    private static final String ICE_MACHINE = "0006BD9B";
    private static final String SUNDAE = "00066666";
    private static final String FERMENT_CABINET = "XXXX";

    public LoraDataHandler(LoraMonitor loraMonitor) {
        super();
        this.client = loraMonitor;

        intIceMachine();
        intFermentCabinet();
    }

    private void intIceMachine() {
        iceMachineState = 0;
        cycles = random.nextInt(0, 200);
    }

    private void intFermentCabinet() {
        fermentCabinetStatus = Sets.newHashSet("0200", "0201", "0202", "0203", "0206", "0222", "0220", "0223");
        fermentCabinetAlarms = Sets.newHashSet("0208", "0209", "020A", "020B", "020C", "020D", "020E", "020F", "0210", "0211", "0212", "0213");
    }

    /**
     * 保存所有的连接信息
     */
    private Map<String, Channel> channelMap = new ConcurrentHashMap<>();
    private Map<String, ScheduledFuture> futureMap = new ConcurrentHashMap<>();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LOG.info("NettyIceHandler channelActive");
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String mClientIp = ipSocket.getAddress().getHostAddress();
        if (null == mClientIp || "".equals(mClientIp.trim())) {
            return;
        }
        channelMap.put(mClientIp, ctx.channel());
        LOG.info("NettyIceHandler channelActive ip{}", mClientIp);
//        ScheduledFuture scheduledFuture = schedule.scheduleAtFixedRate(
//                new SundaeRSTask(ctx.channel()), 5, 5, TimeUnit.SECONDS);

//        futureMap.put(ctx.channel().id().asLongText(), scheduledFuture);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        LOG.info("NettyIceHandler channelInactive");
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String mClientIp = ipSocket.getAddress().getHostAddress();
        if (null == mClientIp || mClientIp.trim().equals("")) {
            return;
        }
        channelMap.remove(mClientIp, ctx.channel());

        LOG.info("连接被断开...");
        futureMap.remove(ctx.channel().id().asLongText()).cancel(false);

        // 使用过程中断线重连
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(new Runnable() {
            @Override
            public void run() {
                // 重连
                client.monitor();
            }
        }, 5, TimeUnit.SECONDS);
        super.channelInactive(ctx);

    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            LOG.info("NettyIceHandler channelRead0 msg:" + new Gson().toJson(msg));
            // 直接转成对象
            DataResponse response = null;
            DataRequest request = (DataRequest) msg;

            switch (request.getLoraId()) {
                case CHAIN_OVEN_PARTS:
                    response = chainOvenPartsRes(request);
                    break;
                case ICE_MACHINE_MD:
                case ICE_MACHINE:
                    response = iceMachineRes(request);
                    break;
                case FERMENT_CABINET:
                    response = fermentCabinetRes(request);
                    break;
                case SUNDAE:
                    response = sundaeRes(request);
                    break;
                default:
                    break;
            }
            if (response != null) {
                ctx.channel().writeAndFlush(response);
            }
        } finally {
            // 必须释放msg数据
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOG.info("NettyIceHandler exceptionCaught", cause);
        if (ctx != null && ctx.channel() != null) {
            ctx.channel().close();
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        LOG.info("NettyIceHandler userEventTriggered");
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                LOG.warn("channel read idle, we will close channel");
                Channel channel = ctx.channel();
                if (null != channel) {
                    channel.disconnect();
                    channel.close();
                    channel = null;
                }
            }
        }
    }

    private DataResponse chainOvenPartsRes(DataRequest request) {
        String address = request.getData().substring(4, 8);
        DataResponse response = new DataResponse();
        StringBuffer sb = new StringBuffer();
        sb.append(request.getData().substring(0, 4));
        sb.append("02");
        switch (address) {
            case "0003":
                sb.append(ByteUtil.decimal2fitHex(random.nextInt(35900,36900), 4));
                break;
            case "000A":
                sb.append(ByteUtil.decimal2fitHex(100, 4));
                break;
            case "0004":
                sb.append(ByteUtil.decimal2fitHex(60, 4));
                break;
            default:
                sb.append(ByteUtil.decimal2fitHex(random.nextInt(0, 65535), 4));
                break;
        }

        response.setData(ModBusUtils.getSendBuf(sb.toString()));
        response.setStationId(request.getStationId());
        response.setLoraId(request.getLoraId());
        return response;
    }

    private DataResponse sundaeRes(DataRequest request) {
        DataResponse response = new DataResponse();
        StringBuffer sb = new StringBuffer();
        taylor(request, sb);
        spaceman(request, sb);
        response.setData(ModBusUtils.getSendBuf(sb.toString()));
        response.setStationId(request.getStationId());
        response.setLoraId(request.getLoraId());
        return response;
    }

    private void spaceman(DataRequest request, StringBuffer sb) {
        //左状态
        if (request.getData().startsWith("AA55000490000094")) {
            sb.append("AA55002A9140AFE4364190723A0000000000000000");
            sb.append(ByteUtil.decimal2fitHex(cycles++, 8));
            sb.append(ByteUtil.decimal2fitHex(cycles++, 8));
            sb.append("010E0000008000070000000000000000DC");
        }
        //右状态
        if (request.getData().startsWith("AA55000491000095")) {
            sb.append("AA55002A9140B2F474419137480000000000000000");
            sb.append(ByteUtil.decimal2fitHex(cycles++, 8));
            sb.append(ByteUtil.decimal2fitHex(cycles++, 8));
            sb.append("010E000000800007000000000000000001");
        }

    }

    private void taylor(DataRequest request, StringBuffer sb) {
        //状态
        if (request.getData().startsWith("F1C0050A0000F2")) {
            sb.append("F4F1C0580A");
            for (int i = 0; i < 82; i++) {
                if (i == 3) {
                    i++;
                    //热处理的处理阶段（0 = 无, 1 =加热阶段, 2 = 保持阶段, 3 =冷却阶段）
                    sb.append(ByteUtil.decimal2fitHex(1, 2));
                    sb.append("00");
                }else if (i == 9) {
                    i++;
                    //（0 = OFF, 3 = 清洗, 5 = 待机, 6 = 自动, 7 = 热处理）
                    sb.append(ByteUtil.decimal2fitHex(4, 2));
                    sb.append("00");
                }else if (i == 11) {
                    i++;
                    //（0 = OFF, 3 = 清洗, 5 = 待机, 6 = 自动, 7 = 热处理）
                    sb.append(ByteUtil.decimal2fitHex(0, 2));
                    sb.append("00");
                } else if (i == 13) {
                    i++;
                    //温度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                } else if (i == 15) {
                    i++;
                    //温度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                }else if (i == 17) {
                    i++;
                    //温度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                }else if (i == 19) {
                    i++;
                    //温度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                } else if (i == 23) {
                    i++;
                    //黏度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                } else if (i == 25) {
                    i++;
                    //黏度
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                    sb.append("00");
                }else if (i == 29) {
                    i++;
                    //锁定状态 （0 = 刷洗完成, 1 =继续, 2 = 锁定）
                    sb.append(ByteUtil.decimal2fitHex(1, 2));
                    sb.append("00");
                }else if (i == 31) {
                    i++;
                    //锁定类型（ 0 = 没有锁定, 2 = 热处理没有执行, 3 = 刷洗超时, 4 = 热处理失败, 5 = 温度传感器错误, 6 = 热处理中止, 7 = 产品超过允许的温度
                    sb.append(ByteUtil.decimal2fitHex(0, 2));
                    sb.append("00");
                }else if (i == 33) {
                    i++;
                    sb.append(ByteUtil.decimal2fitHex(cycles++, 2));
                    sb.append("00");
                } else if (i == 35) {
                    i++;
                    sb.append(ByteUtil.decimal2fitHex(cycles++, 2));
                    sb.append("00");
                } else if (i == 37) {
                    i++;
                    sb.append(ByteUtil.decimal2fitHex(cycles++, 2));
                    sb.append("00");
                }else if (i == 39) {
                    sb.append(ByteUtil.decimal2fitHex(0, 2));
                } else {
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
                }
            }
            sb.append("00F2");
        }
        //热循环
        if (request.getData().startsWith("F1C005080091F2")) {
            sb.append("F4F1C01508");
            for (int i = 0; i < 15; i++) {
                sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
            }
            sb.append("00F2");
        }
        //错误信息
        if (request.getData().startsWith("F1C0050F00FFF2")) {
            sb.append("F4F1C00D0F");
            for (int i = 0; i < 7; i++) {
                sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
            }
            sb.append("00F2");
        }
        //清洗状态
        if (request.getData().startsWith("F1C0050B00C4F2")) {
            sb.append("F4F1C00D0B");
            for (int i = 0; i < 7; i++) {
                sb.append(ByteUtil.decimal2fitHex(random.nextInt(1, 100), 2));
            }
            sb.append("00F2");
        }

    }

    private DataResponse iceMachineRes(DataRequest request) {
        String address = request.getData().substring(4, 8);
        String count = request.getData().substring(8, 12);
        short len = Short.parseShort(count, 16);

        DataResponse response = new DataResponse();
        StringBuffer sb = new StringBuffer();
        sb.append(request.getData().substring(0, 4));
        sb.append(ByteUtil.decimal2fitHex(len * 2, 2));
        sb.append(genIceMachineData(address, count));
        response.setData(ModBusUtils.getSendBuf(sb.toString()));
        response.setStationId(request.getStationId());
        response.setLoraId(request.getLoraId());
        return response;
    }

    private DataResponse fermentCabinetRes(DataRequest request) {
        String address = request.getData().substring(4, 8);
        String count = request.getData().substring(8, 12);
        short len = Short.parseShort(count, 16);

        DataResponse response = new DataResponse();
        StringBuffer sb = new StringBuffer();
        sb.append(request.getData().substring(0, 4));
        sb.append(ByteUtil.decimal2fitHex(len * 2, 2));
        if (fermentCabinetStatus.contains(address)) {
            sb.append(ByteUtil.decimal2fitHex(1, 4));
            sb.append("00");
            response.setData(sb.toString());
        } else if (fermentCabinetAlarms.contains(address)) {
            sb.append(ByteUtil.decimal2fitHex(0, 4));
            sb.append("00");
            response.setData(sb.toString());
        } else {
            sb.append(genfermentCabinetData(address, count));
            response.setData(ModBusUtils.getSendBuf(sb.toString()));
        }

        response.setStationId(request.getStationId());
        response.setLoraId(request.getLoraId());
        return response;
    }

    private String genfermentCabinetData(String address, String count) {
        StringBuffer sb = new StringBuffer();
        short len = Short.parseShort(count, 16);

        switch (address) {
            //设备类型
            case "0002":
                //XR60CX 0x0F01
                //XR75CX 0x12c1
                sb.append(ByteUtil.decimal2fitHex(0x12c1, 4));
                len--;
                break;
            default:
                break;
        }
        for (int i = 0; i < len; i++) {
            sb.append(ByteUtil.decimal2fitHex(random.nextInt(0, 600), 4));
        }

        return sb.toString();
    }

    private String genIceMachineData(String address, String count) {
        StringBuffer sb = new StringBuffer();
        short len = Short.parseShort(count, 16);

        switch (address) {
            //制冰机状态
            case "0002":
            case "0001":
                iceMachineState++;
                if (iceMachineState > 10) {
                    iceMachineState = 0;
                }
                sb.append(ByteUtil.decimal2fitHex(iceMachineState, 4));
                len--;
                break;
            //制冰机循环次数
            case "0008":
                cycles++;
                sb.append(ByteUtil.decimal2fitHex(iceMachineState, 4));
                len--;
                break;
            //Manitowoc制冰机错误码
            case "00AD":
                for (int i = 0; i < len; i = i + 2) {
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(0, 60), 8));
                }
                len = 0;
                break;
            //MD制冰机错误码
            case "000C":
                for (int i = 0; i < len; i++) {
                    sb.append(ByteUtil.decimal2fitHex(random.nextInt(0, 2), 4));
                }
                len = 0;
                break;
            //MD制冰机错误码
            case "0018":
                for (int i = 0; i < len; i++) {
                    sb.append(ByteUtil.decimal2fitHex(1, 4));
                }
                len = 0;
                break;
            default:
                break;
        }
        for (int i = 0; i < len; i++) {
            sb.append(ByteUtil.decimal2fitHex(random.nextInt(0, 600), 4));
        }

        return sb.toString();
    }

}
